#
# coding: utf-8

import warnings
from decimal import Decimal


def _todecimal(value, msg=''):
    if isinstance(value, Decimal):
        return value

    if isinstance(value, float):
        if not msg:
            msg = 'Please consider using str instead of float.'
        warnings.warn(msg, UserWarning)

        return Decimal(str(value))

    if isinstance(value, int):
        return Decimal(value)

    try:
        return Decimal(str(value))
    except:
        raise TypeError('Cannot converte to Decimal：%s' % value.__repr__())


def currency(value, symbol='', places=2, separator=',', point='.', positive=False):
    '''
    Convert decimal to currency string.

    places:    number of digits after the decimal point
    symbol:    optional currency symbol before the sign
    separator: optional grouping separator (comma, period, space, or blank)
    point:     decimal point indicator (comma or period)
               only specify as blank when places is zero
    positive:  optional sign for positive numbers: '+'
    '''

    value = _todecimal(value)

    positive = '+' if positive else ''

    q = Decimal(10) ** - places
    sign, digits, exp = value.quantize(q).as_tuple()
    result = []
    digits = [str(s) for s in digits]
    build, next = result.append, digits.pop
    for i in range(places):
        build(next() if digits else '0')
    if places:
        build(point)
    if not digits:
        build('0')
    i = 0
    while digits:
        build(next())
        i += 1
        if i == 3 and digits:
            i = 0
            build(separator)
    build('-' if sign else positive)
    build(symbol)
    return ''.join(reversed(result))


def cncapital(value, capital=True, prefix=False, classical=True):
    '''
    人民币数字转汉字表示 Ver 0.03
    作者: qianjin(AT)ustc.edu
    版权声明:
        只要保留本代码最初作者的电子邮件即可，随便用。用得爽的话，不反对请
    作者吃一顿。

    参数:
    capital:    True   大写汉字金额
                False  普通汉字金额
    classical:  True   圆
                False  元
    prefix:     True   以'人民币'开头
                False, 无开头
    '''

    # 转换为Decimal
    msg = '''
        由于浮点数精度问题，请使用字符串，或者 decimal.Decimal 类。
        因使用浮点数造成误差而带来的可能风险和损失作者概不负责。
        '''
    value = _todecimal(value, msg)

    # 截断多余小数
    value = Decimal(value).quantize(Decimal('0.01'))

    # 默认大写金额用圆，一般汉字金额用元
    if classical is None:
        classical = True if capital else False

    # 汉字金额前缀
    if prefix is True:
        prefix = '人民币'
    else:
        prefix = ''

    # 汉字金额字符定义
    dunit = ('角', '分')
    if capital:
        num = ('零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖')
        iunit = [None, '拾', '佰', '仟', '万', '拾', '佰', '仟',
                 '亿', '拾', '佰', '仟', '万', '拾', '佰', '仟']
    else:
        num = ('〇', '一', '二', '三', '四', '五', '六', '七', '八', '九')
        iunit = [None, '十', '百', '千', '万', '十', '百', '千',
                 '亿', '十', '百', '千', '万', '十', '百', '千']

    if classical:
        iunit[0] = '圆' if classical else '元'


    # 处理负数
    if value < 0:
        prefix += '负'          # 输出前缀，加负
        value = - value         # 取正数部分，无须过多考虑正负数舍入
                                # assert - value + value == 0

    # 转化为字符串
    s = str(value)

    if len(s) > 19:
        raise ValueError('金额太大了，不知道该怎么表达。')

    istr, dstr = s.split('.')           # 小数部分和整数部分分别处理
    istr = istr[::-1]                   # 翻转整数部分字符串

    so = []     # 用于记录转换结果

    # 零
    if value == 0:
        return prefix + unm[0] + iunit[0]

    haszero = False     # 用于标记零的使用
    if dstr == '00':
        haszero = True  # 如果无小数部分，则标记加过零，避免出现“圆零整”

    # 处理小数部分
    # 分
    if dstr[1] != '0':
        so.append(dunit[1])
        so.append(num[int(dstr[1])])
    else:
        so.append('整')         # 无分，则加“整”
    # 角
    if dstr[0] != '0':
        so.append(dunit[0])
        so.append(num[int(dstr[0])])
    elif dstr[1] != '0':
        so.append(num[0])       # 无角有分，添加“零”
        haszero = True          # 标记加过零了

    # 无整数部分
    if istr == '0':
        if haszero:             # 既然无整数部分，那么去掉角位置上的零
            so.pop()
        so.append(prefix)       # 加前缀
        so.reverse()            # 翻转
        return ''.join(so)

    # 处理整数部分
    for i, n in enumerate(istr):
        n = int(n)
        if i % 4 == 0:          # 在圆、万、亿等位上，即使是零，也必须有单位
            if i == 8 and so[-1] == iunit[4]:   # 亿和万之间全部为零的情况
                so.pop()                        # 去掉万
            so.append(iunit[i])
            if n == 0:                          # 处理这些位上为零的情况
                if not haszero:                 # 如果以前没有加过零
                    so.insert(-1, num[0])       # 则在单位后面加零
                    haszero = True              # 标记加过零了
            else:                               # 处理不为零的情况
                so.append(num[n])
                haszero = False                 # 重新开始标记加零的情况
        else:                                   # 在其他位置上
            if n != 0:                          # 不为零的情况
                so.append(iunit[i])
                so.append(num[n])
                haszero = False                 # 重新开始标记加零的情况
            else:                               # 处理为零的情况
                if not haszero:                 # 如果以前没有加过零
                    so.append(num[0])
                    haszero = True

    # 最终结果
    so.append(prefix)
    so.reverse()
    return ''.join(so)


def incometax(value):
    '''
    个人所得税，2011年9月1日税率，起征点3500元。
    '''
    TAX_BASE = 3500
    TAX_RATE = (
        (    0, Decimal('0.03'),     0),
        ( 1500, Decimal('0.10'),   105),
        ( 4500, Decimal('0.20'),   555),
        ( 9000, Decimal('0.25'),  1005),
        (35000, Decimal('0.30'),  2755),
        (55000, Decimal('0.35'),  5505),
        (80000, Decimal('0.45'), 13505)
        )
    value = _todecimal(value)
    value = value.quantize(Decimal('0.01')) - TAX_BASE
    if value <= 0:
        return 0
    rate = 0
    sub = 0
    for d, r, s in TAX_RATE:
        if d < value:
            rate = r
            sub = s
        else:
            break
    return (value * rate - sub).quantize(Decimal('0.01'))


def pretax(value):
    '''
    税前工资及个人所得税计算，返回税前工资和所得税
    '''
    value = _todecimal(value)
    income = value
    while True:
        t = incometax(income)
        if abs(income - value - t) < 0.001:
            break
        income = value + t
    return income, t

class Currency(Decimal):
    '''
    Chinese Currency
    '''

    def __new__(cls, value):
        inst = Decimal.__new__(cls, value)
        return inst

    def __repr__(self):
        return "%s('%s')" % (self.__class__.__name__, self.__str__())

    def __add__(self, value):
        return self.__class__(Decimal(self).__add__(value))

    def __radd__(self, value):
        return self.__class__(Decimal(self).__radd__(value))

    def __sub__(self, value):
        return self.__class__(Decimal(self).__sub__(value))

    def __rsub__(self, value):
        return self.__class__(Decimal(self).__rsub__(value))

    def __mul__(self, value):
        return self.__class__(Decimal(self).__mul__(value))

    def __rmul__(self, value):
        return self.__class__(Decimal(self).__rmul__(value))

    def __floordiv__(self, value):
        return self.__class__(Decimal(self).__floordiv__(value))

    def __rfloordiv__(self, value):
        return self.__class__(Decimal(self).__rfloordiv__(value))

    def __truediv__(self, value):
        return self.__class__(Decimal(self).__truediv__(value))

    def __rtruediv__(self, value):
        return self.__class__(Decimal(self).__rtruediv__(value))

    def __neg__(self):
        return self.__class__(Decimal(self).__neg__())

    def currency(self, symbol=True):
        if symbol is True:
            symbol = '\u00a5'
        elif symbol in (False, None):
            symbol = ''
        return currency(self.__str__(), symbol)

    def _capital(self, capital=True, prefix=False, classical=True):
        return cncapital(self.__str__(), capital, prefix, classical)

    capital = _capital

class Salary(object):
    '''
    薪资
    value   薪资数额
    intax   True 含税
            False 不含税
    '''

    def __init__(self, value, intax=True):
        value = Currency(value)
        if intax:
            self._tax = Currency(incometax(value))
            self._pretax = value
            self._posttax = value - self.tax
        else:
            self._pretax, self._tax = pretax(value)
            self._tax = Currency(self._tax)
            self._posttax = value

    @property
    def tax(self):
        '''个人所得税税额'''
        return self._tax

    @property
    def pretax(self):
        '''税前金额'''
        return self._pretax

    @property
    def posttax(self):
        '''税后金额'''
        return self._posttax
